﻿using System;
using System.Collections;
using System.Linq;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using SLSearchNavSync.HelperClasses;

namespace SLSearchNavSync.TimerJobs
{
    /// <summary>
    /// Search Navigation Sync Timer Job Definition
    /// </summary>
    internal class SearchNavigationSyncTimerJobDefinition : SPJobDefinition
    {
        #region Persisted Properties

        /// <summary>
        /// The URL for the Search Center site
        /// </summary>
        [Persisted] public string SearchCenterUrl = "";

        /// <summary>
        /// Flag to add synchronize all site collections
        /// </summary>
        [Persisted] public bool SynchronizeAllSiteCollections;

        /// <summary>
        /// The site collections that need to be updated
        /// </summary>
        [Persisted] public ArrayList SiteCollectionsUrls;

        /// <summary>
        /// The sites that should be ignored
        /// </summary>
        [Persisted] public ArrayList ExcludedSiteUrls;

        /// <summary>
        /// Flag to add "This Site" contextual search
        /// </summary>
        [Persisted] public bool AddThisSite;

        /// <summary>
        /// Flag for making "This Site" the first option in the search dropdown (otherwise it's last)
        /// </summary>
        [Persisted] public bool MakeThisSiteFirst;
      
        #endregion

        #region Overridden Properties

        /// <summary>
        /// The Description of the timer job
        /// </summary>
        public override string Description
        {
            get { return Definitions.SearchNavigationSyncTimerJobDescription; }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Constructor for the SearchNavigationSyncTimerJobDefinition Timer job
        /// </summary>
        public SearchNavigationSyncTimerJobDefinition()
        {
        }

        /// <summary>
        /// Constructor for the SearchNavigationSyncTimerJobDefinition Timer job
        /// </summary>
        /// <param name="jobName">Name to use for the timer job</param>
        /// <param name="service">The service to run the timer job on</param>
        /// <param name="server">The server to run the timer job on</param>
        /// <param name="targetType">The locktype to define how the job executes</param>
        public SearchNavigationSyncTimerJobDefinition(string jobName, SPService service, SPServer server,
                                                      SPJobLockType targetType)
            : base(jobName, service, server, targetType)
        {
        }

        /// <summary>
        /// Constructor for the SearchNavigationSyncTimerJobDefinition Timer job
        /// </summary>
        /// <param name="jobName">Name to use for the timer job</param>
        /// <param name="webApplication">The web application to use with the timer job</param>
        public SearchNavigationSyncTimerJobDefinition(string jobName, SPWebApplication webApplication)
            : base(jobName, webApplication, null, SPJobLockType.Job)
        {
            Title = Definitions.SearchNavigationSyncTimerJobTitle;
        }

        #endregion

        /// <summary>
        /// The Timer Job's execute handler
        /// This handles all the work for the Timer job
        /// </summary>
        /// <param name="contentDbId">the current id of the content database</param>
        public override void Execute(Guid contentDbId)
        {
            try
            {
                UlsHelper.WriteUlsEntry("*** Start ***", TraceSeverity.Verbose, EventSeverity.Information,
                    Definitions.UlsCategory);

                //ensure the fields are all configured else throw an error
                if ((string.IsNullOrEmpty(SearchCenterUrl)) ||
                    ((SiteCollectionsUrls == null || SiteCollectionsUrls.Count <= 0) && !SynchronizeAllSiteCollections))
                {
                    throw new Exception(string.Format("Timer job not configured properly"));
                }

                SPSecurity.RunWithElevatedPrivileges(delegate
                {
                    using (var searchSite = new SPSite(SearchCenterUrl))
                    {
                        using (var searchWeb = searchSite.OpenWeb())
                        {
                            //synchronizing all site collections
                            if (SynchronizeAllSiteCollections)
                            {
                                foreach (SPSite targetSite in WebApplication.Sites)
                                {
                                    try
                                    {
                                        SyncSettingsToWebs(targetSite, searchWeb);
                                    }
                                    finally
                                    {
                                        targetSite.Dispose();
                                    }
                                }
                            }
                            else
                            {
                                //sync just requested site collections
                                foreach (string siteUrl in SiteCollectionsUrls)
                                {
                                    using (var targetSite = new SPSite(siteUrl))
                                    {
                                        SyncSettingsToWebs(targetSite, searchWeb);
                                    }
                                }
                            }

                            Update();
                        }
                    }
                });

                UlsHelper.WriteUlsEntry("*** End ***", TraceSeverity.Verbose, EventSeverity.Information,
                    Definitions.UlsCategory);
            }
            catch (Exception ex)
            {
                UlsHelper.WriteUlsEntry(ex, TraceSeverity.Unexpected, String.Empty, Definitions.UlsCategory);
            }
        }

        /// <summary>
        /// Handles the synchronization of search navigation settings to 
        /// each web that isn't in the exclusion list
        /// </summary>
        /// <param name="targetSite">The SiteCollection being sync'd</param>
        /// <param name="searchWeb">The web object for the search center</param>
        private void SyncSettingsToWebs(SPSite targetSite, SPWeb searchWeb)
        {
            foreach (SPWeb targetWeb in targetSite.AllWebs)
            {
                try
                {
                    if (!SiteIsInExclusionList(targetWeb))
                    {
                        SearchSettingsHelper.SyncSearchNavigation(searchWeb,
                            targetWeb,
                            AddThisSite, MakeThisSiteFirst);
                        UlsHelper.WriteUlsEntry(
                            string.Format("Updated search navigation for: {0}",
                                targetWeb.Url),
                            TraceSeverity.Verbose,
                            EventSeverity.Information,
                            Definitions.UlsCategory);
                    }
                }
                finally
                {
                    if (targetWeb != null) targetWeb.Dispose();
                }
            }
        }

        /// <summary>
        /// Checks if a URL is contained in the Exclusion list
        /// </summary>
        /// <param name="webToCheck">SPWeb object for site to check</param>
        /// <returns>true if the site is the exclusion list, false otherwise</returns>
        private bool SiteIsInExclusionList(SPWeb webToCheck)
        {
            try
            {
                var fullWebUrl = webToCheck.Url.ToLower().Trim('/');

                if (ExcludedSiteUrls.Cast<string>().Any(item => item.ToLower().Trim('/') == fullWebUrl))
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                UlsHelper.WriteUlsEntry(ex, TraceSeverity.Unexpected, String.Empty, Definitions.UlsCategory);
            }

            return false;
        }
    }
}
